---
title: Astro Container API (experimental)
sidebar:
  label: Container API (experimental)
i18nReady: true
---
import Since from '~/components/Since.astro'

<p><Since v="4.9.0" /></p>

The Container API allows you to render Astro components in isolation.

This experimental server-side API unlocks a variety of potential future uses, but is currently scoped to allow [testing of `.astro` component output](/en/guides/testing/#vitest-and-container-api) in `vite` environments such as `vitest`.

It also allows you to [manually load rendering scripts](#adding-a-renderer-manually) for creating containers in pages rendered on demand or other "shell" environments outside of `vite` (e.g. inside a PHP or Elixir application).

This API allows you to [create a new container](#create), and render an Astro component returning [a string](#rendertostring) or a [`Response`](#rendertoresponse). 

This API is experimental and subject to breaking changes, even in [minor or patch releases](/en/upgrade-astro/#semantic-versioning). Please consult [the Astro CHANGELOG](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md) for changes as they occur. This page will always be updated with the most current information for the latest version of Astro.

## `create()`

<p>

**Type:** `(options?: AstroContainerOptions) => Promise<experimental_AstroContainer>`
</p>

Creates a new instance of the container. 

```js
import { experimental_AstroContainer } from "astro/container";

const container = await experimental_AstroContainer.create();
```

It accepts an object with the following options:


```ts
export type AstroContainerOptions = {
	streaming?: boolean;
	renderers?: AddServerRenderer[];
};

export type AddServerRenderer =
	| {
			renderer: NamedSSRLoadedRendererValue;
			name: never;
	  }
	| {
			renderer: SSRLoadedRendererValue;
			name: string;
	  };
```

### `streaming` option

<p>

**Type:** `boolean`<br />
**Default:** `false`
</p>

Enables rendering components using [HTML streaming](/en/guides/on-demand-rendering/#html-streaming).

### `renderers` option

<p>

**Type:** `AddServerRenderer[]`<br />
**Default:** `[]`
</p>

A list of loaded client renderers required by the component. Use this if your `.astro` component renders any [UI framework components](/en/guides/framework-components/) or MDX using an official Astro integration (e.g. React, Vue, etc.).

Renderers can be added through the Container API automatically for static applications, or cases where the container isn't called at runtime (e.g. testing with `vitest`).

For [on-demand rendered applications](/en/guides/on-demand-rendering/), or cases where the container is called at runtime or inside other "shells" (e.g. PHP, Ruby, Java, etc.), renderers must be manually imported.

#### Adding a renderer through the Container API

For each official Astro integration, import and use the `getContainerRenderer()` helper function to expose its client and server rendering scripts. These are available for `@astrojs/react`, `@astrojs/preact`, `@astrojs/solid-js`, `@astrojs/svelte`, `@astrojs/vue`, and `@astrojs/mdx`.

For renderer packages outside the `@astrojs` npm org, look in their documentation for `getContainerRenderer()` or a similar function provided.

When using `vite` (`vitest`, Astro integrations, etc.), the renderers are loaded with the function `loadRenderers()` from the virtual module `astro:container`.
 
:::caution
Outside `vite` or for on-demand usage, you'll have to [load the renderers manually](#adding-a-renderer-manually).
:::

The following example provides the necessary object to render an Astro component that renders a React component and a Svelte component: 

```js
import { getContainerRenderer as reactContainerRenderer } from "@astrojs/react";
import { getContainerRenderer as svelteContainerRenderer } from "@astrojs/svelte";
import { loadRenderers } from "astro:container";

const renderers = await loadRenderers([reactContainerRenderer(), svelteContainerRenderer()]);
const container = await experimental_AstroContainer.create({
    renderers
})
const result = await container.renderToString(ReactWrapper);
```

#### Adding a renderer manually

When the container is called at runtime, or inside other "shells", the `astro:container` virtual module's helper functions are not available.  You must import the necessary server and client renderers manually and store them inside the container using `addServerRenderer` and `addClientRenderer`.

Server renderers are required to build your project, and must be stored in the container for every framework used. Client renderers are additionally needed to any hydrate client-side components using [`client:*` directives](/en/reference/directives-reference/#client-directives).

Only one import statement is needed per framework. Importing a renderer makes both the server and client renderers available to your container. However, **server renderers must be added to your container before client renderers**. This allows your entire container to render first, and then hydrate any interactive components.

The following example manually imports the necessary server renderers to be able to display static Vue components and `.mdx` pages. It additionally adds both server and client renderers for interactive React components.

```js
import reactRenderer from "@astrojs/react/server.js";
import vueRenderer from "@astrojs/vue/server.js";
import mdxRenderer from "@astrojs/mdx/server.js";

const container = await experimental_AstroContainer.create();
container.addServerRenderer({ renderer: vueRenderer });
container.addServerRenderer({ renderer: mdxRenderer });

container.addServerRenderer({ renderer: reactRenderer });
container.addClientRenderer({ name: "@astrojs/react", entrypoint: "@astrojs/react/client.js" });
```

## `renderToString()`

<p>

**Type:** <code>(component: AstroComponentFactory; options?: <a href="#rendering-options">ContainerRenderOptions</a>) => Promise&lt;string&gt;</code>
</p>

This function renders a specified component inside a container. It takes an Astro component as an argument and it returns a string that represents the HTML/content rendered by the Astro component.

```js
import { experimental_AstroContainer } from "astro/container";
import Card from "../src/components/Card.astro";

const container = await experimental_AstroContainer.create();
const result = await container.renderToString(Card);
```

Under the hood, this function calls [`renderToResponse()`](#rendertoresponse) and `Response.text()`. 

It also accepts an object as a second argument that can contain a [number of options](#rendering-options).

## `renderToResponse()`

<p>

**Type:** <code>(component: AstroComponentFactory; options?: <a href="#rendering-options">ContainerRenderOptions</a>) => Promise&lt;Response&gt;</code>
</p>

It renders a component, and it returns a `Response` object.

```js
import { experimental_AstroContainer } from "astro/container";
import Card from "../src/components/Card.astro";

const container = await experimental_AstroContainer.create();
const result = await container.renderToResponse(Card);
```

It also accepts an object as a second argument that can contain a [number of options](#rendering-options).

## Rendering options

Both [`renderToResponse()`](#rendertoresponse) and [`renderToString()`](#rendertostring) accept an object as their second argument:

```ts
export type ContainerRenderOptions = {
	slots?: Record<string, any>;
	props?: Record<string, unknown>;
	request?: Request;
	params?: Record<string, string | undefined>;
	locals?: App.Locals;
	routeType?: RouteType;
	partial?: boolean;
};
```

These optional values can be passed to the rendering function in order to provide additional information necessary for an Astro component to properly render.

### `slots`

<p>

**Type**: `Record<string, any>`
</p>

An option to pass content to be rendered with [`<slots>`](/en/basics/astro-components/#slots).

If your Astro component renders one default slot, pass an object with `default` as the key:

```js name="Card.test.js"
import Card from "../src/components/Card.astro";

const result = await container.renderToString(Card, { 
  slots: { default: "Some value" }
});
```

If your component renders named slots, use the slot names as the object keys:

```astro name="Card.astro"
---
---
<div>
  <slot name="header" />
  <slot name="footer" />
</div>
```

```js name="Card.test.js"
import Card from "../src/components/Card.astro";

const result = await container.renderToString(Card, { 
  slots: {
    header: "Header content",
    footer: "Footer"
  }
});
```

You can also render components in cascade:

```astro name="Card.astro"
---
---
<div>
  <slot name="header" />
  <slot name="footer" />
</div>
```

```js name="Card.test.js"
import Card from "../src/components/Card.astro";
import CardHeader from "../src/components/CardHeader.astro";
import CardFooter from "../src/components/CardFooter.astro";

const result = await container.renderToString(Card, { 
  slots: { 
    header: await container.renderToString(CardHeader), 
    footer:  await container.renderToString(CardFooter)
  }
});
```

### `props` option

<p>

**Type**: `Record<string, unknown>`
</p>

An option to pass [properties](/en/basics/astro-components/#component-props) for Astro components.

```js name="Card.test.js"
import Card from "../src/components/Card.astro";

const result = await container.renderToString(Card, { 
  props: { name: "Hello, world!" }
});
```

```astro name="Card.astro"
---
// For TypeScript support
interface Props {
  name: string;
};

const { name } = Astro.props;
---
<div>
  {name}
</div>
```

### `request` option

<p>

**Type**: `Request`
</p>

An option to pass a `Request` with information about the path/URL the component will render.

Use this option when your component needs to read information like `Astro.url` or `Astro.request`.

You can also inject possible headers or cookies.

```js file="Card.test.js"
import Card from "../src/components/Card.astro";

const result = await container.renderToString(Card, { 
  request: new Request("https://example.com/blog", {
    headers: {
      "x-some-secret-header": "test-value"
    }
  })
});
```

### `params` option

<p>

**Type**: `Record<string, string | undefined>`
</p>

An object to pass information about the path parameter to an Astro component responsible for [generating dynamic routes](/en/guides/routing/#dynamic-routes).

Use this option when your component needs a value for `Astro.params` in order to generate a single route dynamically.

```astro name="Card.astro"
---
const { locale, slug } = Astro.params;
---
<div></div>
```

```js file="LocaleSlug.test.js"
import LocaleSlug from "../src/components/[locale]/[slug].astro";

const result = await container.renderToString(LocaleSlug, { 
  params: {
    locale: "en",
    slug: "getting-started"
  }
});
```

### `locals` options

<p>

**Type**: `App.Locals`
</p>

An option to pass information from [`Astro.locals`](/en/reference/api-reference/#locals) for rendering your component.

Use this option to when your component needs information stored during the lifecycle of a request in order to render, such as logged in status.

```astro name="Card.astro"
---
const { checkAuth } = Astro.locals;
const isAuthenticated = checkAuth();
---
{isAuthenticated ? <span>You're in</span> : <span>You're out</span> }
```

```js file="Card.test.js"
import Card from "../src/components/Card.astro";

test("User is in", async () => {
  const result = await container.renderToString(Card, { 
    locals: {
      checkAuth() { return true; }
    }
  });
  
  // assert result contains "You're in"
});


test("User is out", async () => {
  const result = await container.renderToString(Card, { 
    locals: {
      checkAuth() { return false; }
    }
  });
  
  // assert result contains "You're out"
});
```

### `routeType` option

<p>

**Type**: `RouteType`
</p>

An option available when using `renderToResponse()` to specify that you are rendering an [endpoint](/en/guides/endpoints/):

```js
container.renderToString(Endpoint, { routeType: "endpoint" });
```

```js file="endpoint.test.js"
import * as Endpoint from "../src/pages/api/endpoint.js";

const response = await container.renderToResponse(Endpoint, { 
  routeType: "endpoint"
});
const json = await response.json();
```

To test your endpoint on methods such as `POST`, `PATCH`, etc., use the `request` option to call the correct function:

```js file="endpoint.js"
export function GET() {}

// need to test this
export function POST() {}
```

```js file="endpoint.test.js" ins={5-7}
import * as Endpoint from "../src/pages/api/endpoint.js";

const response = await container.renderToResponse(Endpoint, { 
    routeType: "endpoint",
    request: new Request("https://example.com", {
      method: "POST" // Specify POST method for testing
    })
});
const json = await response.json();
```

### `partial` option

<p>

**Type:** `boolean`<br />
**Default:** `true`<br/>
<Since v="4.16.6" />
</p>

Whether or not the Container API renders components as if they were [page partials](/en/basics/astro-pages/#page-partials). This is usually the behavior you want when rendering `components.boolean` so you can render components without a full page shell.

To render a component as a full Astro page, including `<!DOCTYPE html>`, you can opt-out of this behavior by setting `partial` to `false`:

```js name="Card.test.js" ins={4}
import Blog from "../src/pages/Blog.astro";

const result = await container.renderToString(Card, {
    partial: false 
});
console.log(result) // includes `<!DOCTYPE html>` at the beginning of the HTML
```
